# _*_coding:utf-8_*_
"""
   ATS 1.0(Automated Test Server, Version 1.0) Linear test plan module,
   Output data structure is MTL, which is input for EEManager
   Developed by Ivy.Li 2013.03.18.
   (C)Copyright 2013 Hech. All rights reserved
"""
import os
import sys

from . import ppf
from . import sspf
from . import wdf
from . import pmf
from . import gdf

from utils.log_util import loggings as logger
from ATS.datamanager import DbImpl
from ATS.datamanager import DbServer
import traceback
from ATS.util import constant
from ATS.eeManager import task as task
from ATS.globaldata import gdata
# from ATS.proberdriver import Prober
from ATS.proberdriver import cascade_prober as Prober
import copy

workpath = os.path.dirname(os.path.realpath(sys.argv[0]))
sys.path.insert(0, os.path.dirname(workpath))
# error code define
NO_PPF_FOUND = 1
NO_SSPF = 2
NO_ERR = 0
# Main task list, the output interface of linearplan package
MTL = []
Key_Index = 0


def generate_loc_index(*location):
    global Key_Index
    Key_Index = Key_Index + 1
    LMD = gdata.getLMD()
    LMD[Key_Index] = tuple(*location)
    return Key_Index


def get_location(ind):
    LMD = gdata.getLMD()
    if ind in LMD:
        return LMD[ind]
    else:
        return None


# Record myLog when linearize test plan
log_report = ""


def add_log2Report(msg):
    global log_report
    log_report += "%s\n" % msg


def output_MTL():
    global MTL
    for t in MTL:
        t.output()


def output_LMD():
    global Key_Index
    LMD = gdata.getLMD()
    for i in range(1, Key_Index + 1, 1):
        print(i, '<--location-->', LMD[i])


def clearLMD():
    gdata.clearLMD()
    global Key_Index
    Key_Index = 0


def getMTL():
    global MTL
    return MTL


def add2MTL(task_obj):
    global MTL
    MTL.append(task_obj)


def addTask(task_obj, tasklist1=None, tasklist2=None):
    if tasklist1 is not None:
        tasklist1.append(task_obj)
    elif tasklist2 is not None:
        tasklist2.append(task_obj)
    else:
        add2MTL(task_obj)


def clearMTL():
    global MTL
    if MTL:
        del MTL[:]
    MTL = []


#
def linearize_sspf(sspf_obj, wafer=None, siteID='Target', siteCoord='(Tx,Ty)', subsite_name=None):
    """
    Parse SSPF object, generate task and add to MTL.
    """
    for ss in sspf_obj.ssites:
        if subsite_name is not None and subsite_name.lower() != ss.lower():
            continue
        ssites_lowerlist = [ss_tmp.lower() for ss_tmp in sspf_obj.subsites.keys()]
        if not ss.lower() in ssites_lowerlist:
            continue

        # check whether subsite needs to repeat run
        if ss in sspf_obj.repeat:  # loop subsite
            ssite_tasks = []
        else:
            ssite_tasks = None

        # add subsite begin UAP
        if 'UAP_SUBSITE_BEGIN' in sspf_obj.subsites[ss]['uaps']:
            ssite_begin_uap = task.UAPTask(sspf_obj.subsites[ss]['uaps']['UAP_SUBSITE_BEGIN'], task.SUBSITE_LEVEL)
            addTask(ssite_begin_uap, ssite_tasks)
        #
        if not sspf_obj.subsites[ss]['parallel']:
            for dev in sspf_obj.subsites[ss]['devs']:
                tests = sspf_obj.subsites[ss]['tests']
                uaps = sspf_obj.subsites[ss]['uaps']
                if dev in sspf_obj.devs:
                    if 'tests' in sspf_obj.devs[dev] and sspf_obj.devs[dev]['tests']:
                        tests = sspf_obj.devs[dev]['tests']
                    if 'uaps' in sspf_obj.devs[dev] and sspf_obj.devs[dev]['uaps']:
                        uaps = sspf_obj.devs[dev]['uaps']
                # check whether the dev is a loop unit
                if dev in sspf_obj.repeat:  # loop dev
                    dev_tasks = []
                else:
                    dev_tasks = None

                if 'UAP_DEVICE_BEGIN' in uaps:
                    UAP_DEVICE_BEGIN = task.UAPTask(uaps['UAP_DEVICE_BEGIN'], task.DEV_LEVEL)
                    addTask(UAP_DEVICE_BEGIN, dev_tasks, ssite_tasks)

                # calculate test location
                if subsite_name is None:
                    ssname = ss
                else:
                    ssname = subsite_name

                for test in tests:
                    if 'UAP_TEST_BEGIN' in uaps:
                        UAP_TEST_BEGIN = task.UAPTask(uaps['UAP_TEST_BEGIN'], task.TEST_LEVEL)
                        addTask(UAP_TEST_BEGIN, dev_tasks, ssite_tasks)
                    tst_name = test.split('.')[0]
                    location = wafer, siteID, siteCoord, ssname, dev, tst_name
                    ind = generate_loc_index(location)
                    ttask = task.TestTask(test, ind)
                    if tst_name in sspf_obj.repeat:  # loop subsite
                        ttask = task.Loop_Task(tst_name, [ttask], sspf_obj.repeat[tst_name])
                    addTask(ttask, dev_tasks, ssite_tasks)
                    if 'UAP_TEST_END' in uaps:
                        UAP_TEST_END = task.UAPTask(uaps['UAP_TEST_END'], task.TEST_LEVEL)
                        addTask(UAP_TEST_END, dev_tasks, ssite_tasks)
                if 'UAP_DEVICE_END' in uaps:
                    UAP_DEVICE_END = task.UAPTask(uaps['UAP_DEVICE_END'], task.DEV_LEVEL)
                    addTask(UAP_DEVICE_END, dev_tasks, ssite_tasks)

                # check whether the dev is a loop unit
                if dev_tasks is not None:
                    t = task.Loop_Task(dev, dev_tasks, sspf_obj.repeat[dev])
                    addTask(t, ssite_tasks)
        # if parallel execute the devices
        # 并行测试
        else:
            list4devtasks = []
            for dev in sspf_obj.subsites[ss]['devs']:
                devtasks = []
                tests = sspf_obj.subsites[ss]['tests']
                uaps = sspf_obj.subsites[ss]['uaps']
                if dev in sspf_obj.devs:
                    if 'tests' in sspf_obj.devs[dev] and sspf_obj.devs[dev]['tests']:
                        tests = sspf_obj.devs[dev]['tests']
                    if 'uaps' in sspf_obj.devs[dev] and sspf_obj.devs[dev]['uaps']:
                        uaps = sspf_obj.devs[dev]['uaps']

                if 'UAP_DEVICE_BEGIN' in uaps:
                    UAP_DEVICE_BEGIN = task.UAPTask(uaps['UAP_DEVICE_BEGIN'], task.DEV_LEVEL)
                    devtasks.append(UAP_DEVICE_BEGIN)
                    ssname = ss
                else:
                    ssname = subsite_name

                for test in tests:
                    if 'UAP_TEST_BEGIN' in uaps:
                        UAP_TEST_BEGIN = task.UAPTask(uaps['UAP_TEST_BEGIN'], task.TEST_LEVEL)
                        devtasks.append(UAP_TEST_BEGIN)
                    tst_name = test.split('.')[0]
                    location = wafer, siteID, siteCoord, ssname, dev, tst_name
                    ind = generate_loc_index(location)
                    ttask = task.TestTask(test, ind)
                    devtasks.append(ttask)
                    if 'UAP_TEST_END' in uaps:
                        UAP_TEST_END = task.UAPTask(uaps['UAP_TEST_END'], task.TEST_LEVEL)
                        devtasks.append(UAP_TEST_END)
                if 'UAP_DEVICE_END' in uaps:
                    UAP_DEVICE_END = task.UAPTask(uaps['UAP_DEVICE_END'], task.DEV_LEVEL)
                    devtasks.append(UAP_DEVICE_END)
                list4devtasks.append(devtasks)
            pt = task.Parallel_Task(list4devtasks, sspf_obj.subsites[ss]['devs'])
            addTask(pt, ssite_tasks)
        if 'UAP_SUBSITE_END' in sspf_obj.subsites[ss]['uaps']:
            ssite_end_uap = task.UAPTask(sspf_obj.subsites[ss]['uaps']['UAP_SUBSITE_END'], task.SUBSITE_LEVEL)
            addTask(ssite_end_uap, ssite_tasks)

        if ssite_tasks:  # repeat run subsite
            t = task.Loop_Task(ss, ssite_tasks, sspf_obj.repeat[ss])
            add2MTL(t)


def linearize_ppf(ppf_name):
    print("ppf_name", ppf_name)
    global site_begin_uap, site_end_uap, wafer_end_uap, wafer_begin_uap
    clearMTL()
    clearLMD()
    gdata.clearGDD()
    gdata.getGDD().update({'PPF': ppf_name})
    f = os.path.join(constant.PPF_PATH, ppf_name)
    if not os.path.exists(f):
        return NO_PPF_FOUND
    ppf_obj = ppf.PPF(f)
    print("ppf_obj", ppf_obj.error)
    if ppf_obj.error > 0:
        if ppf_obj.error > 1000:
            add_log2Report("Parse PPF file[%s] warning, warning code:%s" % (f, ppf_obj.error))
        else:
            return ppf_obj.error

    # read PMF file in ppf
    if ppf_obj.pmf is not None:
        abs_path = f = os.path.join(constant.PMF_PATH, ppf_obj.pmf)
        pmf_obj = pmf.PMF(abs_path)
        if pmf_obj.error > 0:
            add_log2Report("Parse PMF file[%s] warning, warning code:%s" % (ppf_obj.pmf, pmf_obj.error))
            pmf_obj = None
    else:
        pmf_obj = None
    if pmf_obj is not None:
        gdata.updateGDD(pmf_obj.pins)

    if ppf_obj.gdf is not None:
        abs_path = os.path.join(constant.GDF_PATH, ppf_obj.gdf)
        gdf_obj = gdf.GDF(abs_path)
        if gdf_obj.error > 0:
            add_log2Report("Parse GDF file[%s] warning, warning code:%s" % (ppf_obj.gdf, gdf_obj.error))
            gdf_obj = None
    else:
        gdf_obj = None
    if gdf_obj is not None:
        gdata.updateGDD(gdf_obj.data)

    # read user abort uap and add it to global data
    if 'UAP_USER_ABORT' in ppf_obj.casset_uaps:
        user_abort_uap = ppf_obj.casset_uaps['UAP_USER_ABORT']
        gdata.updateGDD({'User_Abort_UAP': user_abort_uap})
    print("设置探针台需要执行的动作")
    prb_load_task = task.PrbTask(Prober.PR_LOAD)
    prb_unload_task = task.PrbTask(Prober.PR_UNLOAD)
    prb_chuck_up_task = task.PrbTask(Prober.PR_CHUCKUP)
    prb_chuck_down_task = task.PrbTask(Prober.PR_CHUCKDOWN)  # changed by ivan
    #    prb_chuck_up_task = task.PrbTask(Prober.PR_CHUCK,*[1])#chuck up
    #    prb_chuck_down_task = task.PrbTask(Prober.PR_CHUCK,*[0])#chuck down
    # prb_relreturn_task = task.PrbTask(Prober.PR_RELRETURN)
    prb_move_task_dict = {}
    # prb_move_task_dict = task.PrbTask(Prober.PR_RELMOVE)
    # print("prb_move_task_dict", prb_move_task_dict)
    print("探针台动作设置完毕")
    prb_relmove_task_dict = {}
    # Loop wafers
    print("ppf_obj.wafers", ppf_obj.wafers)
    for w in ppf_obj.wafers:
        wp_dict = ppf_obj.wafer_plans
        print("wp_dict", wp_dict)
        # find correct sspf, wdf, gdf, uaps
        if w in wp_dict:
            wdf_file = wp_dict[w]['WDF']
            if wdf_file is None:
                wdf_file = ppf_obj.wdf
            sspf_file = wp_dict[w]['SSPF']
            if sspf_file is None:
                sspf_file = ppf_obj.sspf
            uaps = wp_dict[w]['uaps']
            if not uaps:
                uaps = ppf_obj.casset_uaps
        else:
            wdf_file = ppf_obj.wdf
            sspf_file = ppf_obj.sspf
            gdf_file = ppf_obj.gdf
            uaps = ppf_obj.casset_uaps

        if 'UAP_WAFER_BEGIN' in uaps:
            wafer_begin_uap = task.UAPTask(uaps['UAP_WAFER_BEGIN'], task.WAFER_LEVEL)
        if 'UAP_SITE_BEGIN' in uaps:
            site_begin_uap = task.UAPTask(uaps['UAP_SITE_BEGIN'], task.SITE_LEVEL)
        if 'UAP_SITE_END' in uaps:
            site_end_uap = task.UAPTask(uaps['UAP_SITE_END'], task.SITE_LEVEL)
        if 'UAP_WAFER_END' in uaps:
            wafer_end_uap = task.UAPTask(uaps['UAP_WAFER_END'], task.WAFER_LEVEL)
        # parse sspf, wdf
        if sspf_file:
            abs_path = os.path.join(constant.SSPF_PATH, sspf_file)
            sspf_obj = sspf.SSPF(abs_path)
            if 0 < sspf_obj.error < 1000:
                return sspf_obj.error
            if sspf_obj.error > 0 and sspf_obj.error > 1000:
                add_log2Report("Parse SSPF file[%s] warning, warning code:%s" % (sspf_file, sspf_obj.error))
        else:
            sspf_obj = None

        if wdf_file:
            abs_path = os.path.join(constant.WDF_PATH, wdf_file)
            wdf_obj = wdf.WDF(abs_path)
            if 0 < wdf_obj.error < 1000:
                return wdf_obj.error
            if wdf_obj.error > 0 and wdf_obj.error > 1000:
                add_log2Report("Parse WDF file[%s] warning, warning code:%s" % (wdf_file, wdf_obj.error))
        else:
            wdf_obj = None

        if sspf_obj is None:
            return NO_SSPF
        if wdf_obj:
            sspf_obj.set_ssites(wdf_obj.ssites_seq)

        data_model = gdata.get_service(constant.DATA_MODEL_INSTANCE)
        if data_model.db is None:
            try:
                #                flag,data_con = DbImpl.fetch_data_con_by(ppf_name)
                curr_db_i = gdata.get_service(constant.CURR_DB_FILE)
                flag, data_con = DbImpl.fetch_data_con_by_file(curr_db_i)
            except:
                logger.error("Error while fetching data connection instance:\n%s" % traceback.format_exc())
            else:
                data_model.set_connection(data_con)

        output_models_dic = {constant.OUTPUT_MODELS_KEY: copy.deepcopy(sspf_obj.tests)}
        gdata.register_service(output_models_dic)
        data_model.register_output_models(sspf_obj.tests)
        #        data_model.release_con()
        if not wdf_obj.sites:  # subsite level running or below
            subsites_num = len(wdf_obj.subsites)

            for subsite in wdf_obj.subsites:
                if subsites_num > 1:
                    if not subsite[1] in prb_relmove_task_dict:
                        prb_relmove_task = task.PrbTask(Prober.PR_RELMOVE, *[subsite[1][0], subsite[1][1]])
                        prb_relmove_task_dict[subsite[1]] = prb_relmove_task
                    else:
                        prb_relmove_task = prb_relmove_task_dict[subsite[1]]
                    add2MTL(prb_relmove_task)
                    add2MTL(prb_chuck_up_task)
                linearize_sspf(sspf_obj, wafer=w, subsite_name=subsite[0])
                if subsites_num > 1:
                    add2MTL(prb_chuck_down_task)
        else:  # wafer level automation run

            x_die_size, y_die_size, x_start_pos, y_start_pos, units = \
                wdf_obj.header['diesizex'], wdf_obj.header['diesizey'], \
                eval(wdf_obj.header['target'])[0], eval(wdf_obj.header['target'])[1], wdf_obj.header['units']

            prb_init_task = task.PrbTask(Prober.PR_INIT,
                                         *[x_die_size, y_die_size, x_start_pos, y_start_pos, units])
            if ppf_obj.wafers.index(w) == 0:  # First Wafer, call lot begin uap if needed
                # add UAP_LOT_BEGIN to MTL
                if 'UAP_LOT_BEGIN' in ppf_obj.casset_uaps:
                    a_task = task.UAPTask(ppf_obj.casset_uaps['UAP_LOT_BEGIN'], task.LOT_LEVEL)
                    add2MTL(a_task)
                # add prb_load_task to MTL
                if not constant.IGNORE_1st_WAFER_LOAD:
                    add2MTL(prb_load_task)
                    add2MTL(prb_init_task)
            else:
                add2MTL(prb_load_task)
                add2MTL(prb_init_task)
            # add PRINIT to
            if 'UAP_WAFER_BEGIN' in uaps:
                add2MTL(wafer_begin_uap)
            for site in wdf_obj.sites:
                if not site[1] in prb_move_task_dict:
                    prb_move_task = task.PrbTask(Prober.PR_MOVE, *[site[1][0], site[1][1]])
                    prb_move_task_dict[site[1]] = prb_move_task
                else:
                    prb_move_task = prb_move_task_dict[site[1]]
                add2MTL(prb_move_task)

                if 'UAP_SITE_BEGIN' in uaps:
                    add2MTL(site_begin_uap)
                # add2MTL(prb_relreturn_task)
                for subsite in wdf_obj.subsites:
                    if not subsite[1] in prb_relmove_task_dict:
                        prb_relmove_task = task.PrbTask(Prober.PR_RELMOVE, *[subsite[1][0], subsite[1][1]])
                        prb_relmove_task_dict[subsite[1]] = prb_relmove_task
                    else:
                        prb_relmove_task = prb_relmove_task_dict[subsite[1]]
                    add2MTL(prb_relmove_task)
                    add2MTL(prb_chuck_up_task)

                    linearize_sspf(sspf_obj, wafer=w, siteID=site[0], siteCoord=site[1], subsite_name=subsite[0])
                    add2MTL(prb_chuck_down_task)

                if 'UAP_SITE_END' in uaps:
                    add2MTL(site_end_uap)
            # add2MTL(prb_unload_task)
            if 'UAP_WAFER_END' in uaps:
                add2MTL(wafer_end_uap)

    if 'UAP_LOT_END' in ppf_obj.casset_uaps:
        a_task = task.UAPTask(ppf_obj.casset_uaps['UAP_LOT_END'], task.LOT_LEVEL)
        add2MTL(a_task)

    #    #at the end of automation, move to (0,0)
    #    prb_move_task = task.PrbTask(Prober.PR_MOVE,*[0,0])
    #    add2MTL(prb_move_task)
    #    output_MTL()
    curr_db_i = gdata.get_service(constant.CURR_DB_FILE)
    DbServer.init_dbServer(curr_db_i)
    return NO_ERR
